using GLDotNet;
using GLDotNet.Toolkit;
using System;
using System.Runtime.InteropServices;

namespace GLDotNetDemo
{	
	public class GLDotNetDemo : GLApplication
	{
        [StructLayout(LayoutKind.Sequential)]
        struct Vertex
        {
            public Vector3 Position;

            public Color3 Color;

            public Vector2 UV;
        }

        Vertex[] vertexData = new Vertex[]
        {
            new Vertex() { Position = new Vector3(-0.5f, -0.5f, 0.0f), Color = new Color3(1.0f, 0.0f, 0.0f), UV = new Vector2(0.0f, 0.0f) },
            new Vertex() { Position = new Vector3(0.5f, -0.5f, 0.0f), Color = new Color3(0.0f, 1.0f, 0.0f), UV = new Vector2(1.0f, 0.0f) },
            new Vertex() { Position = new Vector3(-0.5f, 0.5f, 0.0f), Color = new Color3(0.0f, 0.0f, 1.0f), UV = new Vector2(0.0f, 1.0f) },

            new Vertex() { Position = new Vector3(-0.5f, 0.5f, 0.0f), Color = new Color3(1.0f, 0.0f, 0.0f), UV = new Vector2(0.0f, 1.0f) },
            new Vertex() { Position = new Vector3(0.5f, 0.5f, 0.0f), Color = new Color3(0.0f, 1.0f, 0.0f), UV = new Vector2(1.0f, 1.0f) },
            new Vertex() { Position = new Vector3(0.5f, -0.5f, 0.0f), Color = new Color3(0.0f, 0.0f, 1.0f), UV = new Vector2(1.0f, 0.0f) },
        };
               
        GraphicsDevice graphicsDevice;

        ShaderProgram shaderProgram;

        VertexArray vertexArray;
        VertexBuffer<Vertex> vertexBuffer;

        Texture2D texture;
        
        float rotation;
        float direction = 1.0f;
        float zoom = 0;

        public GLDotNetDemo()
            : base(new GLContextParams() { VersionMajor = 3, VersionMinor = 3, ForwardCompatible = true })
        {
            this.Window.Title = "GLDotNet Demo - " + (Environment.Is64BitProcess ? "x64" : "x86");
            this.Window.Resize += this.Window_Resize;
            this.Window.KeyPress += this.Window_KeyPress;
            this.Window.KeyDown += this.Window_KeyDown;
            this.Window.MouseMove += this.Window_MouseMove;
            this.Window.MouseButtonDown += this.Window_MouseButtonDown;
            this.Window.MouseWheel += this.Window_MouseWheel;
        }
                
		protected override void Initialize()
		{
            this.graphicsDevice = new GraphicsDevice(this.GLContext);

            this.graphicsDevice.ClearColor(Color4.CornflowerBlue);
            this.graphicsDevice.Enable(EnableCap.Blend);
            this.graphicsDevice.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);

            this.shaderProgram = new ShaderProgram(this.graphicsDevice);

            this.shaderProgram.AttachShader(Shader.CompileFromFile(this.graphicsDevice, ShaderType.VertexShader, "demo.vert"));
            this.shaderProgram.AttachShader(Shader.CompileFromFile(this.graphicsDevice, ShaderType.FragmentShader, "demo.frag"));

            this.shaderProgram.BindAttribLocation(0, "inPosition");
            this.shaderProgram.BindAttribLocation(1, "inColor");
            this.shaderProgram.BindAttribLocation(2, "inUV");

            this.shaderProgram.Link();
            this.shaderProgram.Use();

            this.vertexArray = new VertexArray(this.graphicsDevice);
            this.vertexBuffer = new VertexBuffer<Vertex>(this.graphicsDevice);
            this.vertexBuffer.SetData(this.vertexData, BufferUsage.StaticDraw);
            this.vertexArray.SetAttribs(this.vertexBuffer, typeof(Vertex));
            
            this.texture = Texture2D.FromFile(this.graphicsDevice, "GLDotNet.png", new Texture2DParams()
            {
                ColorKey = GLUtility.MakePixelRGBA(255, 0, 255, 255)
            });
		}

        protected override void Shutdown()
        {
            this.shaderProgram.Dispose();
            this.vertexArray.Dispose();
            this.vertexBuffer.Dispose();
            this.texture.Dispose();
        }

        private void Window_Resize(object sender, EventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("Resize: {0}x{1}", this.Window.Width, this.Window.Height);
            this.graphicsDevice.Viewport = new Viewport(0, 0, this.Window.Width, this.Window.Height);
        }

        private void Window_KeyPress(object sender, KeyPressEventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("KeyPress: {0}", e.KeyChar);
        }

        private void Window_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Escape)
            {
                this.Exit();
            }
            else if (e.Key == Key.R)
            {
                this.rotation = 0.0f;
            }
            else if (e.Key == Key.Left)
            {
                this.direction = -1.0f;
            }
            else if (e.Key == Key.Right)
            {
                this.direction = 1.0f;
            }
        }

        private void Window_MouseMove(object sender, MouseEventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("MouseMove: {0}x{1}", e.X, e.Y);
        }

        private void Window_MouseButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.Button == MouseButton.Left)
            {
                this.direction = -1.0f;
            }
            else if (e.Button == MouseButton.Right)
            {
                this.direction = 1.0f;
            }
        }

        private void Window_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            this.zoom += (float)e.WheelDelta;
        }

        protected override void Update(TimeSpan elapsed)
        {
            this.rotation += 10.0f * (float)elapsed.TotalSeconds * direction;
        }
				
		protected override void Draw()
		{
			this.graphicsDevice.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

            Matrix4 projection = Matrix4.Identity;

            Matrix4 modelView =
                Matrix4.CreateScale(1 + (this.zoom * 0.1f)) *
                Matrix4.CreateRotationZ(MathHelper.ToRadians(this.rotation));

            this.shaderProgram.SetUniformMatrix("uniProjectionMatrix", ref projection);
            this.shaderProgram.SetUniformMatrix("uniModelViewMatrix", ref modelView);

            this.shaderProgram.SetSampler("sampler", 0, this.texture);

            this.graphicsDevice.DrawArrays(this.vertexArray, PrimitiveType.Triangles, 0, this.vertexData.Length);
                        
            this.graphicsDevice.SwapBuffers();
		}

		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		static void Main() 
		{
			using (GLDotNetDemo demo = new GLDotNetDemo())
				demo.Run();
		}
	}
}
